package dungeon;

import be.kuleuven.cs.som.annotate.Basic;
import be.kuleuven.cs.som.annotate.Immutable;
import dungeonElements.Door;
import dungeonElements.TeleportationSquare;
import dungeonTools.Coordinate;
import dungeonTools.Direction;

/**
 * A class of Dungeons that expand in only one dimension. Shafts can not have rocks, 
 * and doors between squares of a shaft.
 * 
 * @invar A shaft can always have his size as its size.
 * 		| canHaveAsSize(xSize(), getYSize(), getZSize())
 * @invar A shaft can have all its squares as squares with there current coordinates.
 * 		| for each square in this shaft:
 * 		|	canHaveSquareAtCoordinate(square, getCoordinateOfSquare(square))
 * 
 * @author Tom Jacobs & Ewoud De Moor
 * @version 3.0
 */
public class Shaft<E extends TeleportationSquare> extends Dungeon<E> {

	/**
	 * Creates a new Shaft. A shaft is a dungeon whose size 
	 * only can vary in one direction.
	 * 
	 * @param direction
	 * 		The direction of this shaft.
	 * @param size
	 * 		The size of this shaft in the given direction.
	 * @post The direction of this shaft is set to the given direction.
	 * 		| if( (direction == Direction.EAST) || (direction == Direction.WEST) )
	 * 		|	then getDirection() == Direction.WEST
	 * 		| if( (direction == Direction.NORTH) || (direction == Direction.SOUTH) )
	 * 		|	then getDirection() == Direction.SOUTH
	 * 		| if( (direction == Direction.CEILING) || (direction == Direction.FLOOR) )
	 * 		|	then getDirection() == Direction.FLOOR
	 * @throws IllegalArgumentException
	 * 		the size must both be positive.
	 * 		| size < 1
	 * @throws IllegalArgumentException
	 * 		The direction must be initialized.
	 * 		| direction == null
	 */
	public Shaft(Direction direction, long size) throws IllegalArgumentException {
		if(size < 1)
			throw new IllegalArgumentException("Size must be positive");
		if(direction == null)
			throw new IllegalArgumentException("Direction must be initialized");
		
		switch(direction) {
		case EAST: case WEST:
			this.direction = Direction.WEST;
			setXSize(size);
			setYSize(1);
			setZSize(1);
			break;
		case NORTH: case SOUTH:
			this.direction = Direction.SOUTH;
			setXSize(1);
			setYSize(size);
			setZSize(1);
			break;
		case CEILING: case FLOOR:
			this.direction = Direction.FLOOR;
			setXSize(1);
			setYSize(1);
			setZSize(size);
			break;
		default:
			throw new IllegalStateException();
		}
	}

	
	/**
	 * Check if this shaft can have the given size as its size.
	 *
	 * @param xSize
	 * 		The xSize to check.
	 * @param ySize
	 * 		The ySize to check.
	 * @param zSize
	 * 		The zSize to check.
	 * @return False if this dungeon can't have the new size.
	 * 		| if(!super.canHaveAsSize(xSize, ySize, zSize))
	 * 		|	then result == false
	 * @return False if the size in a direction other than the direction of this shaft, 
	 * 		is other than 1.
	 * 		| if( (xSize != 1)&&!(getDirection().equals(Direction.WEST)) )
	 * 		| 	then result == false
	 * 		| if( (ySize != 1)&&!(direction.equals(Direction.SOUTH)) )
	 * 		| 	then result == false
	 * 		| if( (zSize != 1)&&!(direction.equals(Direction.FLOOR)) )
	 * 		| 	then result == false
	 * @return False if changing the size could result in a door between squares 
	 * 		in a shaft.
	 * 		| if( (xSize>getXSize() || ySize>getYSize() || zSize>getZSize())
	 * 		|	&& contains(new Coordinate(getXSize()-1, getYSize()-1, getZSize()-1)) 
	 * 		|	&& getSquareAtCoordinate(new Coordinate(getXSize()-1, getYSize()-1, getZSize()-1))
	 * 		|		.getBorder(getDirection().getOppositeDirection()) instanceof Door )
	 * 		|	then result == false
	 */
	@Override
	public boolean canHaveAsSize(long xSize, long ySize, long zSize){
		if(!super.canHaveAsSize(xSize, ySize, zSize))
			return false;
		if( (xSize != 1)&&!(getDirection().equals(Direction.WEST)) )
			return false;
		if( (ySize != 1)&&!(getDirection().equals(Direction.SOUTH)) )
			return false;
		if( (zSize != 1)&&!(getDirection().equals(Direction.FLOOR)) )
			return false;
		//Check door at end:
		if( (xSize>getXSize() || ySize>getYSize() || zSize>getZSize())
				&& contains(new Coordinate(getXSize()-1, getYSize()-1, getZSize()-1)) 
				&& getSquareAtCoordinate(new Coordinate(getXSize()-1, getYSize()-1, getZSize()-1))
					.getBorder(getDirection().getOppositeDirection()) instanceof Door )
			return false;
	
		return true;
	}
	
	
	/**
	 * Returns the direction of this Shaft.
	 * Direction.WEST represents the X-direction.
	 * Direction.SOUTH represents the Y-direction.
	 * Direction.FLOOR represents the Z-direction.
	 */
	@Basic @Immutable 
	public Direction getDirection(){
		return direction;
	}
	
	/**
	 * Variable registering the direction of this square.
	 */
	private final Direction direction;
	
	
	/**
	 * Checks whether or not a square can be placed at a specific coordinate
	 * 
	 * @param square
	 * 		The square to place
	 * @param coordinate
	 * 		The coordinate at which it should be placed
	 * @return False if a dungeon can't have the given square as a square at the 
	 * 		given coordinates.
	 * 		| if(!super.canHaveSquareAtCoordinate(square, coordinate))
	 * @return False if adding the given square would result in doors between the 
	 * 		squares of this shaft.
	 * 		| for each direction in Direction:
	 * 		|	if(getDirection().equals(direction)
	 * 		|		if( (square.getBorder(direction) instanceof Door) 
	 * 		|				&& (!coordinate.equals(Coordinate.ORIGIN)))
	 * 		|			then result == false
	 * 		|		if( (square.getBorder(direction.getOppositeDirection()) instanceof Door)
	 * 		|				&& (!coordinate.equals(new Coordinate(getXSize()-1,getYSize()-1,getZSize()-1))) )
	 * 		|			then result == false
	 */
	@Override
	public boolean canHaveSquareAtCoordinate(E square, Coordinate coordinate){
		if(!super.canHaveSquareAtCoordinate(square, coordinate))
			return false;
		switch(getDirection()) {
		case EAST: case WEST:
			if( (square.getBorder(Direction.WEST) instanceof Door) 
					&& (!coordinate.equals(Coordinate.ORIGIN)))
				return false;
			if( (square.getBorder(Direction.EAST) instanceof Door) 
					&& (!coordinate.equals(new Coordinate(getXSize()-1,getYSize()-1,getZSize()-1))) )
				return false;
			break;
		case NORTH: case SOUTH:
			if( (square.getBorder(Direction.SOUTH) instanceof Door) 
					&& (!coordinate.equals(Coordinate.ORIGIN)) )
				return false;
			if( (square.getBorder(Direction.NORTH) instanceof Door)
					&& (!coordinate.equals(new Coordinate(getXSize()-1,getYSize()-1,getZSize()-1))) )
				return false;
			break;
		case CEILING: case FLOOR:
			if( (square.getBorder(Direction.FLOOR) instanceof Door)
					&& (!coordinate.equals(Coordinate.ORIGIN)) )
				return false;
			if( (square.getBorder(Direction.CEILING) instanceof Door)
					&& (!coordinate.equals(new Coordinate(getXSize()-1,getYSize()-1,getZSize()-1))) )
				return false;
			break;
		default:
			throw new IllegalStateException();
		}
		return true;
	}
	
}
